﻿// <copyright project="Salient.SqlServer.NHibernate" file="IdiotPKCompositeUserType.cs" company="Sky Sanders">
// This source is a Public Domain Dedication.
// Please see http://spikes.codeplex.com/ for details.   
// Attribution is appreciated
// </copyright> 
// <version>1.0</version>
using System;
using System.Data;
using NHibernate;
using NHibernate.Engine;
using NHibernate.Type;
using NHibernate.UserTypes;

namespace Northwind.Tests.Salient
{
    /// <summary>
    /// Learning how to create a composite user type for keys that cannot be mapped native
    /// </summary>
    public class IdiotPKCompositeUserType : ICompositeUserType
    {
        #region ICompositeUserType Members

        /// <summary>
        /// Get the value of a property
        /// </summary>
        /// <param name="component">an instance of class mapped by this "type"</param><param name="property"/>
        /// <returns>
        /// the property value
        /// </returns>
        public object GetPropertyValue(object component, int property)
        {
            var id = (IdiotPk) component;
            if (property == 0)
                return id.SalesOrderId;
            else
                return id.SalesOrderDetailId;
        }

        /// <summary>
        /// Retrieve an instance of the mapped class from a IDataReader. Implementors
        ///             should handle possibility of null values.
        /// </summary>
        /// <param name="dr">IDataReader</param><param name="names">the column names</param><param name="session"/><param name="owner">the containing entity</param>
        /// <returns/>
        public object NullSafeGet(IDataReader dr, string[] names, ISessionImplementor session, object owner)
        {
            object obj0 = NHibernateUtil.Double.NullSafeGet(dr, names[0]);
            object obj1 = NHibernateUtil.String.NullSafeGet(dr, names[1]);
            if (obj0 == null || obj1 == null) return null;
            var SalesOrderID = (int) obj0;
            var SalesOrderDetailID = (int) obj1;
            return new IdiotPk {SalesOrderDetailId = SalesOrderDetailID, SalesOrderId = SalesOrderID};
        }

        /// <summary>
        /// Write an instance of the mapped class to a prepared statement.
        ///             Implementors should handle possibility of null values.
        ///             A multi-column type should be written to parameters starting from index.
        /// </summary>
        /// <param name="cmd"/><param name="value"/><param name="index"/><param name="session"/>
        public void NullSafeSet(IDbCommand cmd, object value, int index, ISessionImplementor session)
        {
            //"SalesOrderID", "SalesOrderDetailID" 

            if (value == null)
            {
                ((IDataParameter) cmd.Parameters[index]).Value = DBNull.Value;
                ((IDataParameter) cmd.Parameters[index + 1]).Value = DBNull.Value;
            }
            else
            {
                var id = (IdiotPk) value;
                ((IDataParameter) cmd.Parameters[index]).Value = id.SalesOrderId;
                ((IDataParameter) cmd.Parameters[index + 1]).Value = id.SalesOrderDetailId;
            }
        }


        /// <summary>
        /// Set the value of a property
        /// </summary>
        /// <param name="component">an instance of class mapped by this "type"</param><param name="property"/><param name="value">the value to set</param>
        public void SetPropertyValue(object component, int property, object value)
        {
            throw new Exception("Immutable!");
        }

        /// <summary>
        /// Compare two instances of the class mapped by this type for persistence
        ///             "equality", ie. equality of persistent state.
        /// </summary>
        /// <param name="x"/><param name="y"/>
        /// <returns/>
        public bool Equals(object x, object y)
        {
            if (ReferenceEquals(x, y))
                return true;
            if (x == null || y == null)
                return false;
            return x.Equals(y);
        }

        /// <summary>
        /// Get a hashcode for the instance, consistent with persistence "equality"
        /// </summary>
        public int GetHashCode(object x)
        {
            return x.GetHashCode();
        }


        /// <summary>
        /// Return a deep copy of the persistent state, stopping at entities and at collections.
        /// </summary>
        /// <param name="value">generally a collection element or entity field</param>
        /// <returns/>
        public object DeepCopy(object value)
        {
            return value;
        }

        /// <summary>
        /// Transform the object into its cacheable representation.
        ///             At the very least this method should perform a deep copy.
        ///             That may not be enough for some implementations, method should perform a deep copy. 
        /// That may not be enough for some implementations, however; for example, associations must be cached as identifier values. (optional operation)
        /// </summary>
        /// <param name="value">the object to be cached</param><param name="session"/>
        /// <returns/>
        public object Disassemble(object value, ISessionImplementor session)
        {
            return value;
        }

        /// <summary>
        /// Reconstruct an object from the cacheable representation.
        ///             At the very least this method should perform a deep copy. (optional operation)
        /// </summary>
        /// <param name="cached">the object to be cached</param><param name="session"/><param name="owner"/>
        /// <returns/>
        public object Assemble(object cached, ISessionImplementor session, object owner)
        {
            return cached;
        }

        /// <summary>
        /// During merge, replace the existing (target) value in the entity we are merging to
        ///             with a new (original) value from the detached entity we are merging. For immutable
        ///             objects, or null values, it is safe to simply return the first parameter. For
        ///             mutable objects, it is safe to return a copy of the first parameter. However, since
        ///             composite user types often define component values, it might make sense to recursively 
        ///             replace component values in the target object.
        /// </summary>
        public object Replace(object original, object target, ISessionImplementor session, object owner)
        {
            return original;
        }

        /// <summary>
        /// Get the "property names" that may be used in a query. 
        /// </summary>
        public string[] PropertyNames
        {
            get { return new[] {"SalesOrderID", "SalesOrderDetailID"}; }
        }

        /// <summary>
        /// Get the corresponding "property types"
        /// </summary>
        public IType[] PropertyTypes
        {
            get
            {
                return new IType[]
                           {
                               NHibernateUtil.Int32, NHibernateUtil.Int32
                           };
            }
        }

        /// <summary>
        /// The class returned by NullSafeGet().
        /// </summary>
        public Type ReturnedClass
        {
            get { return typeof (IdiotPk); }
        }

        /// <summary>
        /// Are objects of this type mutable?
        /// </summary>
        public bool IsMutable
        {
            get { return false; }
        }

        #endregion
    }
}